import { WsMessageBody } from '@common/dto/message.decorator';
import { ApiFail } from '@common/utils';
import { WsServiceExceptionFilter } from '@common/ws-service.exception.filter';
import { WsServiceResponseInterceptor } from '@common/ws-service.response.interceptor';
import { defaultConfig } from '@game/config/config';
import {
  Inject,
  Injectable,
  Logger,
  UseFilters,
  UseInterceptors,
  UsePipes,
  forwardRef,
} from '@nestjs/common';

import {
  MessageBody,
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
  OnGatewayInit,
  OnGatewayConnection,
  OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

import { wsGlobal } from '@game/config/config';
import { RedisService } from '@common/reids/redis.service';
import { getIpLocalAddress, getIpAddress } from '@common/utils/address';
import { SocketService } from './socket/socket.service';
import { RebotService } from './rebot/rebot.service';
import { respData,resp } from './socket/message.utils';
import { async } from 'rxjs';

@Injectable()
@UseFilters(new WsServiceExceptionFilter())
@UseInterceptors(new WsServiceResponseInterceptor())
@WebSocketGateway({
  transports: ['websocket'],
  namespace: '/chatRoom',
  allowEIO3: true,
  cors: {
    origin: '*',
  },
})
export class MyWebSocketGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  constructor(
    public readonly redisService: RedisService,
    public readonly socketService: SocketService,
    @Inject(forwardRef(() => RebotService))
    public readonly rebotService: RebotService,

  ) {}
  private logger: Logger = new Logger('MessageGateway');

  @WebSocketServer()
  server: Server;

  @SubscribeMessage('hello')
  @UsePipes(WsMessageBody)
  hello(client: Socket, @MessageBody() reqData: { name: string }) {
    console.log(reqData.name);

    if (!reqData || !reqData.name) {
      throw new ApiFail(301, 'name null');
    }
    console.log(JSON.stringify(reqData));
    // client.emit('hello',{message:"ok"})
    return reqData;
  }
  // 初始化
  public afterInit(server: Server): void {
    if (!wsGlobal.host) {
      wsGlobal.host_ip = getIpLocalAddress();
      this.redisService.hset({
        key: `-socket-chat_${wsGlobal.host_ip}`,
        value: wsGlobal.host_ip,
        expire: 86400 * 30,
      });
      console.log('内网ip', wsGlobal.host_ip, defaultConfig.NODE_ENV);

      getIpAddress().then((res) => {
        wsGlobal.host = res;
        console.log('外网ip', wsGlobal.host, wsGlobal.port);
        //开发环境存内网  生产环境存外网
        if (defaultConfig.NODE_ENV != 'dev') {
          this.redisService.hset({
            key: `-socket-chat_${wsGlobal.host}`,
            value: wsGlobal.host,
            expire: 86400 * 30,
          });
        }
      });
    }

    return this.logger.log(`Init: ${server.adapter.name}`);
  }

  // 断开连接
  public async handleDisconnect(client: Socket): Promise<void> {
    await this.close(client, {});
    return this.logger.log(`Client disconnected: ${client.id}`);
  }

  // 连接
  public handleConnection(socket: Socket): void {
    const data = {
      nickName: socket.handshake.query.nickName,
      token: socket.handshake.query.token,
      userId: socket.handshake.query.userId,
      socket: socket,
      createTime: new Date().getTime(),
    };
    console.log('新链接来了！', data.userId);

    // const userId = await tokenSerivce.getUserId(data["token"]);
    //存起来不然找不到人了
    // const isDev = defaultConfig.NODE_ENV === 'dev';

    if (data.userId) {
      // console.log("###############", this.socket.id);
      wsGlobal.$socketChat[data.userId + ''] = data;
      // this.redisService.deleteBlack();

      socket.emit('connected', {
        id: socket.id,
        userId: data.userId,
        message: wsGlobal.host + '连接成功',
      });
    } else {
      socket.emit('connected', {
        id: socket.id,
        message: '连接失败',
      });
    }
    return this.logger.log(`Client connected: ${socket.id}`);
  }
  @SubscribeMessage('messageGroup')
  @UsePipes(WsMessageBody)
  async messageGroup(client: Socket, @MessageBody() wsData) {
    const data = {
      nickName: wsData && wsData.nickName,
      userId: wsData && wsData.userId,
      type: (wsData && wsData.type) || 'messageGroup',
      avatar: wsData && wsData.avatar,
      message: wsData && wsData.message,
      roomId: wsData && wsData.roomId,
      ownerId: wsData && wsData.ownerId, //本人 每次都是改客户端的id
      id: wsData && wsData.socketId,
    };
    // 判断存不存在    能不能发的问题 客户端限制
    console.log('messageGroup:', data);
    if (data.roomId) {
      this.socketService.notifyMessageGroup('messageGroup', data);
    } else {
      delete data.roomId;
      data.message = '未创建房间';
      wsGlobal.$socketChat[data.userId].socket.emit('joinGroup', data);
    }
  }
  @SubscribeMessage('message')
  @UsePipes(WsMessageBody)
  async messageAction(client: Socket, @MessageBody() wsData) {
    // console.log(wsData);
    const data = {
      nickName: wsData && wsData.nickName,
      userId: wsData && wsData.userId,
      type: (wsData && wsData.type) || 'message',
      message: wsData && wsData.message,
      ownerId: wsData && wsData.ownerId, //本人 每次都是改客户端的id
      id: wsData && wsData.socketId,
      isAsk: wsData && wsData.isAsk,
    };

    this.socketService.notifyMessage('message', data);
  }
  @SubscribeMessage('send')
  @UsePipes(WsMessageBody)
  async send(@MessageBody() wsData) {
    // console.log(wsData);
    const data = {
      nickName: wsData && wsData.nickName,
      userId: wsData && wsData.userId,
      type: (wsData && wsData.type) || 'message1',
      message: wsData && wsData.message,
      roomId: wsData && wsData.roomId,
      ownerId: wsData && wsData.ownerId, //本人 每次都是改客户端的id
      id: wsData && wsData.socketId,
      isAsk: wsData && wsData.isAsk,
      eventName: respData[(wsData && wsData.type) || 'message1'],
    };
          // 这里需要判断机器人轮次
    // 两种情况  确实用户不存在  还有一种是机器，所以得有个机器列表 处理机器打牌逻辑
    if(!wsGlobal.$socketChat[data.userId]){
      if (
        resp.game_action == data.type 
      ) {
        let data2:any = await this.redisService.hget("roomUsers" + data.roomId);
        if (!data2) {
          console.log("room不存在！！！");
        } else {
          const users = data2;
      

          const currentUser = users.find(
            (item) => item.role == "robot" && item.userId == data.userId
          );
          if (currentUser) {
            //得到机器的消息 内部处理
            if (data.type == resp.game_action) {
              if (
                data.message.hu ||
                data.message.gang ||
                data.message.chi ||
                data.message.peng
              ) {
                setTimeout(async()=>{
                  if (data.message.hu) {
                    await this.rebotService.hu({
                      userId: data.userId,
                      roomId: data.roomId,
  
                    });
                  }
                  if (data.message.gang) {
                    await this.rebotService.gang({
                      userId: data.userId,
                      roomId: data.roomId,
                      pai: data.message.gangpai,
                    });
                  }
                  if (data.message.peng) {
                    await this.rebotService.peng({
                      userId: data.userId,
                      roomId: data.roomId,
                    });
                  }
                  if (data.message.chi) {
                    await this.rebotService.chiAction({
                      userId: data.userId,
                      roomId: data.roomId,
                    });
                  }
                },1500)
                
              } 
              
            }
          } else {
            console.log(data.type,respData[data.type],data.userId + "用户不在线！");
          }
         }
      }
    }else{
     await this.socketService.notifyMessage(data.type, data);
    }
   
      
  }

  @SubscribeMessage('broadcastInRoom')
  @UsePipes(WsMessageBody)
  async broadcastInRoom(@MessageBody() wsData) {
    // console.log(wsData);
    const data = {
      nickName: wsData && wsData.nickName,
      userId: wsData && wsData.userId,
      type: (wsData && wsData.type) || 'messageGroup1',
      avatar: wsData && wsData.avatar,
      message: wsData && wsData.message,
      roomId: wsData && wsData.roomId,
      ownerId: wsData && wsData.ownerId, //本人 每次都是改客户端的id
      id: wsData && wsData.socketId,
      includSender: wsData && wsData.includSender,
      eventName: respData[(wsData && wsData.type) || 'messageGroup1'],

    };

    if (data.roomId) {

       // 这里需要判断机器人轮次
     await this.socketService.notifyMessageGroup(data.type, data,async (specail)=>{
            // 两种情况  确实用户不存在  还有一种是机器，所以得有个机器列表 处理机器打牌逻辑
            if(!wsGlobal.$socketChat[specail.userId]){
              if (
                resp.game_chupai == data.type &&specail.userId==data.userId
              ) {
                console.log(specail.userId,"可能是机器人")

                let data2:any = await this.redisService.hget("roomUsers" + data.roomId);
                if (!data2) {
                  console.log("room不存在！！！");
                } else {
                  const users = data2;
              

                  const currentUser = users.find(
                    (item) => item.role == "robot" && item.userId == specail.userId
                  );
                  if (currentUser) {
                    setTimeout(()=>{
                       //得到机器的消息 内部处理 去掉await卡线程会乱消息
                     this.rebotService.chupai({
                      userId: specail.userId,
                      roomId: data.roomId,
                    });
                    },1500)
                   
                  } else {
                    console.log(data.type,respData[data.type],specail.userId + "用户不在线!！");
                  }
                }
              }
            }
          
      });
    } else {
      delete data.roomId;
      data.message = '未创建房间';
      wsGlobal.$socketChat[data.userId].socket.emit('joinGroup', data);
    }
  }
  @SubscribeMessage('systemMessage')
  @UsePipes(WsMessageBody)
  async systemMessage(client: Socket, @MessageBody() wsData) {
    const data = {
      userId: wsData && wsData.userId,
      type: wsData && wsData.type,
      message: wsData && wsData.message,
      ownerId: wsData && wsData.ownerId,
    };
    console.log(data);
    this.socketService.systemNotify('message', data);
    return 'ok';
  }

  //拆分加入群聊逻辑  新加入 、重连加入 区分管理员
  @SubscribeMessage('joinGroup')
  @UsePipes(WsMessageBody)
  async joinGroup(client: Socket, @MessageBody() wsData) {
    const data: any = {
      nickName: wsData && wsData.nickName + '',
      userId: wsData && wsData.userId + '', //用户id
      ownerId: wsData && wsData.ownerId + '', //房间拥有者id
      id: wsData && wsData.socketId,
      isAsk: wsData && (wsData.isAsk || 0), //房间拥有者id room的权限读取
    };
    // console.log(data);
    // 逻辑拆分这里只需要检验参数 加入群则完事
    if (wsData && wsData.roomId) {
      data.roomId = wsData && wsData.roomId;

      let isNeed = true;
      //判断房间游戏是否开始
      const game = await this.redisService.hget('game' + data.roomId);
      if(game){
        throw new ApiFail(600,'游戏正在进行中！！！')
      }
      if (data.isAsk == 0) {
       
        this.socketService.notifyGroup('joinGroup', data, 'joinGroup');
      } else {
        // 找到群主 通知有人加入 开启授权
        for (const key in wsGlobal.$socketChat) {
          if (wsGlobal.$socketChat[key].userId == data.ownerId) {
            // 通知群主  如果群里不在线 等做一个临时记录 type=1
            wsGlobal.$socketChat[key].socket.emit('joinInvitedGroup', data);
            isNeed = false;
            break;
          }
        }
        // console.log("群主没收到", isNeed);

        // 通知其他服务器这个room里的人
        if (isNeed && data.id) {
          delete data.id;
          this.socketService.emit('joinGroup', data);
        }
      }
    }
  }

  // 重连
  @SubscribeMessage('reconnectGroup')
  @UsePipes(WsMessageBody)
  async reconnectGroup(client: Socket, @MessageBody() wsData) {
    const data: any = {
      roomId: wsData && wsData.roomId + '', //房间id
      userId: wsData && wsData.userId + '', //房间拥有者id
      ownerId: wsData && wsData.ownerId + '', //房间拥有者id
      id: wsData && wsData.socketId,
      status: wsData && wsData.status,
      isAsk: wsData && (wsData.isAsk || 0), //房间拥有者id room的权限读取
    };
    if (wsData && wsData.roomId) {
      //群主  //本人在服务器
      // 判断一下是不是群主 进房间啊
      if (data.userId == data.ownerId) {
        const res = data.isAsk == 1 ? '开启授权' : '';
        data.message = '欢迎群主驾临！' + res;
      }
      console.log(data);
      this.socketService.notifyGroup('joinGroup', data, 'joinGroup');
      //加入房间后 如果有游戏数据 则判断为重连
      const game = await this.redisService.hget('game' + data.roomId);
      if(game && data.status==2){
      this.rebotService.reconnection(game,data)
      }
    } else {
      data.message = '未创建房间';
      wsGlobal.$socketChat[data.userId].socket.emit('joinGroup', data);
      // this.emit("joinGroup", data);
    }
  }
  @SubscribeMessage('exitGroup')
  @UsePipes(WsMessageBody)
  async exitGroup(client: Socket, @MessageBody() wsData) {
    const data: any = {
      nickName: wsData && wsData.nickName,
      type: wsData && wsData.type, //"out"
      userId: wsData && wsData.userId, //
      roomId: wsData && wsData.roomId,
      ownerId: wsData && wsData.ownerId, // 特殊处理  等于自己 则是退群 不等于 则是群主踢人 前端控制权限
      id: wsData && wsData.socketId,
    };
    // console.log(data);
    // 被踢
    //自己退群操作
    if (data.ownerId == data.userId) {
      data.message = '自己退群';
    } else {
      data.message = '管理员踢人' + data.userId;
    }
    this.socketService.exitGroup(data);
  }

  //群主，admin，脚本自动 解散room  为了减轻压力
  @SubscribeMessage('destoryRoom')
  @UsePipes(WsMessageBody)
  async destoryRoom(client: Socket, @MessageBody() wsData) {
    const data: any = {
      roomId: wsData && wsData.roomId,
    };
    console.log(data.roomId, '解散了');
    data.message = '解散了';
    this.socketService.destoryRoom(data);
  }

  // socketiio监听客户端是否活着 自动调用
  async close(socket: Socket, wsData: any) {
    const data = {
      userId: wsData && wsData.userId, //
      id: socket && socket.id,
    };
    console.log('close', data.id);
    if (data.id) {
      for (const key in wsGlobal.$socketChat) {
        if (
          wsGlobal.$socketChat[key].socket &&
          wsGlobal.$socketChat[key].userId &&
          wsGlobal.$socketChat[key].socket.id == socket.id
        ) {
          console.log(
            'close:::',
            wsGlobal.$socketChat[key].userId,
            wsGlobal.$socketChat[key].socket.id,
          );
          // 计算单次用户在线时长 微信太容易close
          // const endTime = +new Date().getTime();
          // await this.model('log').add({
          //   userId: wsGlobal.$socketChat[key].userId,
          //   create_time: wsGlobal.$socketChat[key].createTime,
          //   end_time: endTime,
          //   time: endTime - wsGlobal.$socketChat[key].createTime,
          // });
          //广播掉线通知
          wsGlobal.$socketChat[key].socket.disconnect(true);
          wsGlobal.$socketChat[key].socket.removeAllListeners();
          delete wsGlobal.$socketChat[key];

          data.userId = key;

          break;
        }
      }
    }

    // close的时候 把状态改为0  加入 为1  关闭为0 重连加入为1
    if (data.id) {
      delete data.id;
      this.socketService.emit('close', data);
    }
  }

  // 是否同意动作
  @SubscribeMessage('joinAgreeGroup')
  @UsePipes(WsMessageBody)
  async joinAgreeGroup(client: Socket, @MessageBody() wsData) {
    const data = {
      nickName: wsData && wsData.nickName,
      userId: wsData && wsData.userId, //申请人
      type: wsData && wsData.type, //操作状态 agree
      roomId: wsData && wsData.roomId,
      ownerId: wsData && wsData.ownerId, //群主
      toAdmin: wsData && wsData.toAdmin, //给管理结果的状态
      id: wsData && wsData.socketId,
    };
    // 拆分逻辑  一个是群主 agree 一个用户 agree  先入库再发消息通知
    if (data.type == 'agree') {
      this.socketService.notifyGroup('joinAgreeGroup', data, 'joinGroup');
    } else {
      for (const key in wsGlobal.$socketChat) {
        if (data.toAdmin == 'invited') {
          if (key && key == data.ownerId) {
            // 管理员被拒绝
            wsGlobal.$socketChat[key].socket.emit('joinAgreeGroup', data);
            break;
          }
        } else {
          if (key && key == data.userId) {
            // 用户被拒绝
            wsGlobal.$socketChat[key].socket.emit('joinAgreeGroup', data);
            break;
          }
        }
      }
      if (data.id) {
        delete data.id;
        this.socketService.emit('joinAgreeGroup', data);
      }
    }
  }

  //群主拉人  其他人是否同意动作
  @SubscribeMessage('joinInvitedGroup')
  @UsePipes(WsMessageBody)
  async joinInvitedGroup(client: Socket, @MessageBody() wsData) {
    // 需要存数据库 不然用户记不住啊
    const data = {
      nickName: wsData && wsData.nickName,
      userIds: wsData && wsData.userIds, //申请人的哦
      type: wsData && wsData.type, //操作状态 agree 、other
      roomId: wsData && wsData.roomId,
      ownerId: wsData && wsData.ownerId, //群主、管理员（可能）
      id: wsData && wsData.socketId,
    };
    //判断房间存不在
    data.userIds.forEach((userId) => {
      for (const key in wsGlobal.$socketChat) {
        if (key && key == userId) {
          const data2 = {
            nickName: data.nickName,
            userId: userId, //拉人的哦
            ownerId: data.ownerId,
            type: data.type,
            roomId: data.roomId,
            id: data.id,
          };
          wsGlobal.$socketChat[key].socket.emit('joinInvitedGroup', data2);

          console.log(data);
          break;
        }
      }
    });
    if (data.id) {
      delete data.id;
      this.socketService.emit('joinInvitedGroup', data);
    }
  }
}
